type memory =
{
  data: float list
}

type env_val =
  | Num of float
  | Addr of int
  | Fun of (float list -> memory -> float)
  | Proc of (float list -> memory -> memory)

type env = (string * env_val) list

let getE (id:string) (env:((string * env_val) list)) =
  try
    List.assoc id env
  with
    Not_found -> failwith ("id inconnue: "^id)

let rec getAllE list_id env =
  match list_id with
  | []   -> []
  | h::t -> (getE h env)::(getAllE t env)

let addE id value env =
  (id, value)::env

let rec addAllE list_id list_val env =
  match (list_id, list_val) with
  | ([], [])               -> env
  | ([], _::_) | (_::_,[]) -> 
    failwith "more value or more id than other one"
  | ((x::xq), (v::vq))     -> 
    addAllE xq vq (addE x (Num(v)) env)

let inR num = 
  Num(num)

let inF f =
  Fun(f)

let inP p =
  Proc(p)

let inA a =
  Addr (a)

let outR value =
  match value with
    | Num (x) -> x
    | _       -> failwith "unbound value"

let outF value =
  match value with
    | Fun (f) -> f
    | _       -> failwith "unbound value"

let outP value =
  match value with
    | Proc (p) -> p
    | _       -> failwith "unbound value"

let outA value =
  match value with
    | Addr    (p) -> p
    | _       -> failwith "unbound value"  

let isR value =
  match value with
    | Num (x) -> true
    | _       -> false

let isF value =
  match value with
    | Fun (x) -> true
    | _       -> false

let isP value =
  match value with
    | Proc (x) -> true
    | _       -> false

let isA value =
  match value with
    | Addr (x) -> true
    | _       -> false

let newM mem =
  let newData = mem.data@[0.] in
  let newMemory = {data = newData} in
  ((List.length newData), newMemory)

let setM mem addr n =
  let rec setrec list_tmp new_list =
    match list_tmp with
    | [] -> {data=new_list}
    | a::q -> 
      if((List.length new_list) == (addr-1))
      then setrec q (new_list@[n])
      else setrec q (new_list@[a])
  in setrec mem.data []

let getM mem addr =
  List.nth mem.data (addr-1)
    
let rec printM mem = 
  print_string "memory: "; (printMrec mem)
and printMrec mem =
  match mem.data with
  | []   -> print_endline ""
  | a::q -> (print_float a); print_string ", "; printMrec {data=q}
    
let pi = 4.0 *. atan 1.0
  
let emptyEnv = []

let emptyMem = {data=[pi/.2.; 0.; 0.]}

(* let memTest = {data = [1.; 2.; 3.; 4.]} in *)
(* (setM memTest 2 33.) *)
